//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//
//------------------------------------------------------------------------------
//
//  File: ioctl.c
//
//  This file implements the OEM's IO Control (IOCTL) functions and declares
//  global variables used by the IOCTL component.
//
#include <bsp.h>
#include <fmd.h>
#include <ebootcfg.h>
#include <xllp_pm.h>
#include <xllp_lcd_plat.h>
#include <ioctl_cfg.h>
#include <Pkfuncs.h>
#include <flash_cfg.h>

#include "args.h"
#include "xllp_dfc.h"
#include "nandfmd.h"
#include "Littleton_registry.h"

extern tft_panel_t tft_panel;

//------------------------------------------------------------------------------
//
//  Global: g_oalIoctlPlatformType/OEM
//
//  Platform Type/OEM
//
#if defined( OSV_SP )
DWORD   g_oalIoCtlPlatform     = OAL_IOCTL_PLATFORM_SMARTPHONE;
#elif defined( OSV_PPC )
DWORD   g_oalIoCtlPlatform     = OAL_IOCTL_PLATFORM_POCKETPC;
#else
DWORD   g_oalIoCtlPlatform     = OAL_IOCTL_PLATFORM_WINCE;
#endif
LPCWSTR g_oalIoCtlPlatformType = IOCTL_PLATFORM_TYPE;
LPCWSTR g_oalIoCtlPlatformOEM  = IOCTL_PLATFORM_OEM;

//------------------------------------------------------------------------------
//
//  g_oalIoCtlVendorId
//
//  This is vendor/manufacturer code used to generate device UUID. It should
//  be defined in platform code.
//
const UINT8 g_oalIoCtlVendorId[6]= {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// Default MAC address for RNDIS
static const BYTE g_OEMMacAddress[] = { 0x80, 0x00, 0x60, 0x0F, 0xE8, 0x00 };

//------------------------------------------------------------------------------
//
//  Global: g_oalIoctlProcessorVendor/Name/Core
//
//  Processor information
//
LPCWSTR g_oalIoCtlProcessorVendor = IOCTL_PROCESSOR_VENDOR;
LPCWSTR g_oalIoCtlProcessorName   = IOCTL_PROCESSOR_NAME;
LPCWSTR g_oalIoCtlProcessorCore   = IOCTL_PROCESSOR_CORE;

//------------------------------------------------------------------------------
//
//  Global: g_oalIoctlInstructionSet
//
//  Processor instruction set identifier
//
UINT32 g_oalIoCtlInstructionSet = IOCTL_PROCESSOR_INSTRUCTION_SET;
UINT32 g_oalIoCtlClockSpeed     = IOCTL_PROCESSOR_CLOCK_SPEED;

//------------------------------------------------------------------------------
//
//  Global: g_EBootCfg
//
//  Configuration information stored by EBOOT in flash
//
EBOOT_CFG	g_EBootCfg;
UINT8 tempBuffer[LB_BYTES_PER_PAGE * LB_PAGES_PER_BLOCK]; //[512*32];

//------------------------------------------------------------------------------
//
//  External: ARMCacheInfo
//
//  Processor cache information structure
//
extern const CacheInfo ARMCacheInfo;

#define GetEBootCfg(id)	(g_EBootCfg.drvCfg[id])

BOOL FlashReadCfgPage(BSP_ARGS *pArgs, SectorInfo *pSectorInfo)
{
    BOOL rc = FALSE;
    FlashInfo flashInfo;
    UINT32 sectorsPerBlock, sectorSize, blockSize;
    SECTOR_ADDR sector;
    BLOCK_ID block;
    SectorInfo sectorInfo;

    // Get flash info
    if (!FMD_GetInfo(&flashInfo))
    {
        RETAILMSG(1, (TEXT("ERROR: FlashRead: FMD_GetInfo call failed!\r\n")));
        return rc;
    }

       if (flashInfo.wDataBytesPerSector == LB_BYTES_PER_PAGE )
        block = LB_FLASH_BLOCK_EBOOTCFG_START;
    else
        block = FLASH_BLOCK_EBOOTCFG_START;


    sectorsPerBlock	= flashInfo.wSectorsPerBlock;
    sectorSize = flashInfo.wDataBytesPerSector;
    blockSize = flashInfo.dwBytesPerBlock;

    // Read data from block
    sector = block * sectorsPerBlock;
    if (!FMD_ReadSector(sector, tempBuffer, &sectorInfo, 1))
    {
          // When block read fail, there isn't much we can do more
        RETAILMSG(1, (TEXT("FMD_ReadSector Failure at reading sector %d\r\n"), sector));
        return rc;
    }

    if (pArgs)
    {
        memcpy((void *)pArgs, &tempBuffer[0], sizeof(BSP_ARGS));
        if (pArgs->eBootCFG.ConfigMagicNumber != EBOOT_CFG_MAGIC_NUMBER)
        {
            RETAILMSG(1, (TEXT("Invalid EBoot Cfg number\r\n")));
            return FALSE;
        }
    }


    if (pSectorInfo)
        memcpy((void *)pSectorInfo, &sectorInfo, sizeof(SectorInfo));

    return (TRUE);
}

BOOL FlashWriteCfgPage(BSP_ARGS *pArgs, SectorInfo *pSectorInfo)
{
    SECTOR_ADDR sector;
    UINT8 i;
    FlashInfo flashInfo;
    UINT32 sectorsPerBlock, sectorSize, blockSize;
    BOOL rc = FALSE;
    UINT8 *pTmp;
    int count = 3;

    // Get flash info
    if (!FMD_GetInfo(&flashInfo))
    {
        RETAILMSG(1, (TEXT("ERROR: FlashRead: FMD_GetInfo call failed!\r\n")));
        return rc;
    }

    sectorsPerBlock	= flashInfo.wSectorsPerBlock;
    sectorSize = flashInfo.wDataBytesPerSector;
    blockSize = flashInfo.dwBytesPerBlock;

    do {
              if (flashInfo.wDataBytesPerSector == LB_BYTES_PER_PAGE )
            rc = FMD_EraseBlock(LB_FLASH_BLOCK_EBOOTCFG_START);
        else
        rc = FMD_EraseBlock(FLASH_BLOCK_EBOOTCFG_START);

        count--;
    } while ((rc != TRUE) && (count != 0));

    if (rc == FALSE)
    {
        RETAILMSG(1, (TEXT("FMD_EraseBlock failure\r\n")));
        return rc;
    }

    memset(tempBuffer, 0xFF, sizeof(tempBuffer));
    if (pArgs)
        memcpy(tempBuffer, pArgs, sizeof(BSP_ARGS));

    pTmp = &tempBuffer[0];
       if (flashInfo.wDataBytesPerSector == LB_BYTES_PER_PAGE )
           sector = LB_FLASH_BLOCK_EBOOTCFG_START* sectorsPerBlock;
    else
           sector = FLASH_BLOCK_EBOOTCFG_START* sectorsPerBlock;

    for (i = 0; i < sectorsPerBlock; i++)
    {
        if(!FMD_WriteSector(i+sector, pTmp, pSectorInfo, 1))
            RETAILMSG(1, (TEXT("FMD_WriteSector failure at sector %d\r\n"), sector+i));
        pTmp = pTmp + sectorSize;
    }

    rc = TRUE;

    return rc;
}

BOOL OALIoCtlEdbgSend(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize
)
{
    return FALSE;
}

static void FlashUpdateMode(BOOL fUpdate)
{

    HANDLE handle;
    BSP_ARGS BSP;
    SectorInfo SectorInfo;
    handle = FMD_Init(FMD_CALLER_OAL, NULL, NULL);
    if (!handle)
    {
        RETAILMSG(1, (TEXT("FMD_Init: Failure to init\r\n")));
        return;
    }


    //currently sometimes read fmd will be failed and will cause eboot flag not set
    //now make it retry if read fmd failed 
    //the time-out issue will be fixed after Chao's cs on Zylonite.
    while(!FlashReadCfgPage(&BSP, &SectorInfo))
        RETAILMSG(1, (TEXT("FlashReadCfgPage failed\r\n")));
    //else
    {
        BSP.bUpdateMode= fUpdate;
        FlashWriteCfgPage(&BSP, &SectorInfo);
    }

    RETAILMSG(1, (TEXT("Set EBOOT Flag to %d\r\n"), fUpdate));
    FMD_Deinit(handle);
}

//------------------------------------------------------------------------------
//
//  Function:  SetDeviceDriverFlags
//
//  Create/Modify the "Flags" entry for a device driver
//

static BOOL SetDeviceDriverFlags(LPCWSTR szKeyPath, DWORD flags)
{
    BOOL rc = FALSE;
    HKEY hKey;
    UINT32 value;

    // Open/create key
    if (NKRegCreateKeyEx(
        HKEY_LOCAL_MACHINE, szKeyPath, 0, NULL, 0, 0, NULL,
         &hKey, &value
    ) != ERROR_SUCCESS) goto cleanUp;

    // Set value
    rc = NKRegSetValueEx(
        hKey, L"Flags", 0, REG_DWORD, (UCHAR*)&flags, sizeof(DWORD)
    ) == ERROR_SUCCESS;

    // Close key
    NKRegCloseKey(hKey);

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalGetHWEntropy
//
//  This function retrieves a 64-bit random number based upon an OEM-defined device hardware identifier.
//  Using the Ethernet MAC as reference
//
BOOL OALIoCtlHalGetHWEntropy(
    UINT32 code, VOID *lpInBuf, UINT32 nInBufSize, VOID *lpOutBuf,
    UINT32 nOutBufSize, UINT32 *pOutSize)
{
    if (lpInBuf || nInBufSize || !lpOutBuf || (nOutBufSize < 8)) {
      return (FALSE);
    }

    // use Ethnet MAC address as the unique number.
    {
      OAL_KITL_ARGS *pKITLArgs;
      UCHAR *cp = lpOutBuf;

      pKITLArgs   = (OAL_KITL_ARGS*) OALArgsQuery(OAL_ARGS_QUERY_KITL);

      memcpy(cp, "ZY", 2);
      memcpy(cp+2, pKITLArgs->mac, 6);

      if(pOutSize)
      {
          *pOutSize = 8;
      }

      OALMSG(OAL_FUNC, (TEXT("OALIoCtlHalGetHWEntropy: %02x %02x %02x %02x %02x %02x %02x %02x\r\n"),
                 cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]));

      return (TRUE);
    }
}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalGetUUID
//
//  Implements the IOCTL_HAL_GET_UUID handler.
//
BOOL OALIoCtlHalGetUUID(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize
) {
    BOOL rc = FALSE;
    UINT32 id[2];
    UINT32 tmp;

    OALMSG(OAL_IOCTL&&OAL_FUNC, (L"+OALIoCtlHalGetUUID(...)\r\n"));

    // Differentiate based on platform
    if (g_oalIoCtlPlatform == OAL_IOCTL_PLATFORM_SMARTPHONE ||
        g_oalIoCtlPlatform == OAL_IOCTL_PLATFORM_POCKETPC)
        {
        UCHAR uuid[sizeof(UUID)];

        // Check parameters
        if (pOutSize != NULL)
            {
            *pOutSize = sizeof(uuid);
            }

        if (pOutBuffer == NULL || outSize < sizeof(uuid))
            {
            NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
            OALMSG(OAL_WARN, (L"WARN: OALIoCtlHalGetUUID: Buffer too small\r\n"));
            goto cleanUp;
            }

        // Get hardware IDs
        if(!OALIoCtlHalGetHWEntropy(IOCTL_HAL_GET_HWENTROPY, NULL, 0, id, 8, &tmp))
            goto cleanUp;

        // Store unique device ID.
        uuid[8] = (UINT8)( id[0] >>  0 );
        uuid[9] = (UINT8)( id[0] >>  8 );
        uuid[10] = (UINT8)( id[0] >> 16 );
        uuid[11] = (UINT8)( id[0] >> 24 );
        uuid[12] = (UINT8)( id[1] >>  0 );
        uuid[13] = (UINT8)( id[1] >>  8 );
        uuid[14] = (UINT8)( id[1] >> 16 );
        uuid[15] = (UINT8)( id[1] >> 24 );


        // Lower 4 bits of byte 7 of the Device ID in upper nibble,
        // and 4 bit version in lower nibble.
        // TO Check: only set as 1 can pass LTK7120

        uuid[6] = 1;

        // Upper 4 bits of byte 7 of the Device ID in upper nibble,
        // and 4 bit variant in lower nibble.
        // TO Check: only set as 8 can pass LTK7120
        uuid[7] = 8;



        // 48 bit HW MFG Product Line Number. We are going to use the MS
        // assigned ID for Intel for this.
        uuid[0] = g_oalIoCtlVendorId[0];
        uuid[1] = g_oalIoCtlVendorId[1];
        uuid[2] = g_oalIoCtlVendorId[2];
        uuid[3] = g_oalIoCtlVendorId[3];
        uuid[4] = g_oalIoCtlVendorId[4];
        uuid[5] = g_oalIoCtlVendorId[5];


        // Copy UUID to output buffer
        memcpy(pOutBuffer, &uuid[0], sizeof(uuid));
        }
    else
        {
        GUID GuidPattern = {0x600cc7d0, 0xde3a, 0x4713, {
            0xa5, 0xb0, 0x56, 0xe, 0x6c, 0x36, 0x4e, 0xde
        }};

        VOID* pGuidPattern = &GuidPattern;

        // Check parameters
        if (pOutSize != NULL)
            {
            *pOutSize = sizeof(GuidPattern);
            }

        if (pOutBuffer == NULL || outSize < sizeof(GuidPattern))
            {
            NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
            OALMSG(OAL_WARN, (L"WARN: OALIoCtlHalGetUUID: Buffer too small\r\n"));
            goto cleanUp;
            }

        // Get hardware IDs
        if(!OALIoCtlHalGetHWEntropy(IOCTL_HAL_GET_HWENTROPY, NULL, 0, id, 8, &tmp))
            goto cleanUp;

        // Modify GUID with die IDs
        ((UINT32*)pGuidPattern)[0] ^= id[0];
        ((UINT32*)pGuidPattern)[1] ^= id[1];

        // Copy GUID pattern to output buffer
        memcpy(pOutBuffer, &GuidPattern, sizeof(GUID));
        }

    // Done
    rc = TRUE;

cleanUp:
    // Indicate status
    OALMSG(OAL_IOCTL&&OAL_FUNC, (L"-OALIoCtlHalGetUUID(rc = %d)\r\n", rc));
    return rc;
}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalGetDeviceId
//
//  Implements the IOCTL_HAL_GET_DEVICE_ID handler. This function fills in a
//  DEVICE_ID structure.
//
BOOL OALIoCtlHalGetDeviceId(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize
) {
    BOOL rc = FALSE;
    DEVICE_ID *pId = (DEVICE_ID *)pOutBuffer;
    LPSTR pDeviceId = NULL;
    UINT32 size, length1, length2, offset;

    OALMSG(OAL_IOCTL&&OAL_FUNC, (L"+OALIoctlHalGetDeviceID(...)\r\n"));


#if defined OSV_PPC || defined OSV_SP
    // First, handle the special case where we are called with a buffer size of 16 bytes
    if ( outSize == sizeof(UUID) )
    {
        return (OALIoCtlHalGetUUID(IOCTL_HAL_GET_UUID, pInpBuffer, inpSize, pOutBuffer, outSize, pOutSize));
    }
#endif


    // Get device unique id from arguments
    pDeviceId = OALArgsQuery(OAL_ARGS_QUERY_DEVID);
    if (pDeviceId == NULL) pDeviceId = "";

    // Compute required size (first is unicode, second multibyte!)
    length1 = (NKwcslen(g_oalIoCtlPlatformType) + 1) * sizeof(WCHAR);
    length2 = strlen(pDeviceId) + 1;
    size = sizeof(DEVICE_ID) + length1 + length2;

    __try
    {
        // update size if pOutSize is specified
        if (pOutSize) *pOutSize = size;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        NKSetLastError(ERROR_INVALID_PARAMETER);
        OALMSG(OAL_WARN, (
            L"WARN: OALIoCtlHalGetDeviceID: Invalid out buffer length parameter\r\n"
        ));
        goto cleanUp;
    }


    // Validate inputs (do it after we can return required size)
    if (pOutBuffer == NULL || outSize < sizeof(DEVICE_ID)) {
        NKSetLastError(ERROR_INVALID_PARAMETER);
        OALMSG(OAL_WARN, (
            L"WARN: OALIoCtlHalGetDeviceID: Invalid parameter\r\n"
        ));
        goto cleanUp;
    }

    __try
    {

        // Set size to DEVICE_ID structure
        pId->dwSize = size;

        // If the size is too small, indicate the correct size
        if (outSize < size) {
            NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
            goto cleanUp;
        }

        // Fill in the Device ID type
        offset = sizeof(DEVICE_ID);

        // Copy in PlatformType data
        pId->dwPresetIDOffset = offset;
        pId->dwPresetIDBytes = length1;
        memcpy((UINT8*)pId + offset, g_oalIoCtlPlatformType, length1);
        offset += length1;

        // Copy device id data
        pId->dwPlatformIDOffset = offset;
        pId->dwPlatformIDBytes  = length2;
        memcpy((UINT8*)pId + offset, pDeviceId, length2);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        NKSetLastError(ERROR_INVALID_USER_BUFFER);
        OALMSG(OAL_WARN, (
            L"WARN: OALIoCtlHalGetDeviceID: User buffer access violation!\r\n"
        ));
        goto cleanUp;
    }

        // We are done
    rc = TRUE;

cleanUp:
    // Indicate status
    OALMSG(OAL_IOCTL&&OAL_FUNC, (L"-OALIoCtlHalGetDeviceID(rc = %d)\r\n", rc));
    return rc;
}

//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalGetCacheInfo
//
//  This function returns information about the CPU's instruction and data caches.
//
BOOL OALIoCtlHalGetCacheInfo(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{

    // Validate caller's arguments.
    //
    if (!pOutBuffer || (outSize < sizeof(CacheInfo)))
    {
        NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
        return(FALSE);
    }

    // Copy the cache information into the caller's buffer.
    //
    __try
    {
        memcpy(pOutBuffer, &ARMCacheInfo, sizeof(CacheInfo));
        if (pOutSize) *pOutSize = sizeof(CacheInfo);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        NKSetLastError(ERROR_INVALID_USER_BUFFER);
        OALMSG(OAL_WARN, (
            L"WARN: OALIoCtlHalGetCacheInfo: User buffer access violation!\r\n"
        ));
        return(FALSE);
    }

    return(TRUE);
}

//------------------------------------------------------------------------------
//
//  Function:  IOCTL_HAL_GET_RNDIS_MACADDR
//
//  This IOCTL allows an OEM to specify a MAC address for RNDIS.
//
//  This IOCTL should return one or two MAC addresses, depending on hardware
//  capabilities.
//
//  The USB RNDIS technology connects your device to a network,
//  over USB, via a networked host PC. USB RNDIS actually requires TWO MAC
//  addresses: one for the device itself (device MAC), and one to identify the
//  device on the host PC (host MAC).
//
//  An OEM should find a way to properly implement this IOCTL, or otherwise
//  be aware of the implications of this IOCTL on USB RNDIS:
//
//  1) Recommended. Ideally, a manufacturer will obtain an official, globally
//  unique MAC address and program it onto the device using fuses, EEPROM, etc.
//  If this is the case, only this device MAC address should be returned. The
//  RNDIS driver will autogenerate the host MAC address by setting the 'private'
//  bit. With this approach, two different devices will never conflict on a
//  collision domain.
//
//  2) If a device does not have a unique MAC address, we should try to construct
//  two MAC addresses using unique identifiers on the device, like a CPU ID or
//  flash part serial number. We can then return these two MAC addresses with
//  the 'private' bit set on both. Although the MAC addresses returned are only
//  pseudo-unique, they will greatly reduce the chances of collision.
//
//  3) Without this IOCTL, RNDIS will use a hard-coded, default MAC address. This
//  approach has limitations; multiple 'default' devices that attach to the same
//  collision domain WILL fail.
//
//
//  The Littleton reference board does have a pre-programmed MAC address, however
//  this MAC is intended for debugging through KITL. Simultaneous use of this MAC
//  for KITL and RNDIS is not an option. Also, the LAN91C module is not likely to
//  be present on production hardware.
//
//  Littleton also does not have a CPU ID, Flash part serial number, or other
//  suitable 'serial number'. (This is why UUID, HWEntropy, GetDevice all rely
//  on the LAN91C MAC, and also require changes in production devices).
//
//  Due to Littleton's limitations, this implementation is only for example. We have
//  chosen option 3: return the hard-coded RNDIS MAC address. This is equivalent
//  to having the IOCTL not implemented.
//

BOOL OALIoCtlHalGetRNDISMACADDR(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
    BOOL rc = FALSE;

    if (pOutSize != NULL) *pOutSize = 6;

    // Validate caller's arguments.
    if (!pOutBuffer || (outSize < 6))
        {
        NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
        OALMSG(OAL_WARN, (L"WARN: OALIoCtlHalGetRNdisMacAddr: "
            L"Buffer too small\r\n"
            ));
        goto cleanUp;
        }

    // Copy the cached information into the caller's buffer.
    // OEM should change this global to point to a unique MAC address
    memcpy(pOutBuffer, g_OEMMacAddress, 6);
    if (pOutSize) *pOutSize = 6;

    // Note: Bit 1 (Locally Administered Address bit) in MAC address
    // must not be set. UsbFn driver uses returned address as MAC address
    // on desktop (XP, W2K) side of virtual RNDIS network. On device
    // side same address with set LAA bit is used.

    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalInitRegistryLittleton
//
//  Implements the IOCTL_HAL_INITREGISTRY handler specific to Littleton.
//  The goal here is to update the registry based on the BSP_ARGS set in
//  the bootloader. On Littleton some drivers are exclusive.
//

BOOL OALIoCtlHalInitRegistryLittleton(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize
) {
    BSP_ARGS *pArgs;
    pArgs = (BSP_ARGS *)IMAGE_SHARE_ARGS_UA_START;

    // By default we only call KITL to register its device. Note that on
    // system without KITL this function is stubed...
    OALKitlInitRegistry();

    // Only the hive registry is mounted at this point
    // Set all Flags entries to their defaults from platform.reg
    SetDeviceDriverFlags(STUART_REGKEY, 0);
    SetDeviceDriverFlags(CF_REGKEY,     0);
    SetDeviceDriverFlags(SDMMC_REGKEY,  0);

    // The serial driver has several built-in functions, one of which
    // is output of serial debug messages. If selected, the port
    // is already in use, so we won't load the driver.

    if( pArgs->STUARTUsage & BSP_ARGS_STUART_DEBUG_SERIAL )
    {
        RETAILMSG(1, (L"InitRegistry: Disabling Serial Driver based on EBOOT Config\r\n"));
        if( !SetDeviceDriverFlags(STUART_REGKEY, DEVFLAGS_NOLOAD) )
        {
            RETAILMSG(1, ((L"InitRegistry: ERROR accessing %s\r\n"),STUART_REGKEY));
        }
    }

    return TRUE;
}


//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalPostInit
//
//  This function is the next OAL routine called by the kernel after OEMInit and
//  provides a context for initializing other aspects of the device prior to
//  general boot.
//
BOOL OALIoCtlHalPostInit(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{

    RETAILMSG(1, (L"OALIoCtlHalPostInit\r\n"));
    return(TRUE);
}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalReboot
//
//  This function hardware-reboots the Littleton platform.
//
BOOL OALIoCtlHalReboot(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
    volatile XLLP_PM_SPMU_T *v_pPMURegs;

    NKDbgPrintfW(TEXT("+ALIoCtlHalReboot\r\n"));

    // To do a GPIO reset, we have to set SWGR in the PMCR register.
    v_pPMURegs = (volatile XLLP_PM_SPMU_T *) OALPAtoVA(MONAHANS_BASE_REG_PA_SPMU, FALSE);

    // We must set the PSPR (according to boot ROM docs) to point to our
    // boot vector. We'll use Eboot's physical entrypoint for now (should we use
    // the MOBM?)
    v_pPMURegs->pspr = IMAGE_MOBM_RAM_START; // MOBM entry point

    v_pPMURegs->pmcr |= 0x80000000; // Force GPIO reset

    NKDbgPrintfW(TEXT("-OALIoCtlHalReboot\r\n"));
    return(FALSE);

}

BOOL OALIoCtlUnknown(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{

    // Do nothing for now.
    //
    return(TRUE);

}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlGetDeviceInfo
//
//  Implements the IOCTL_HAL_GET_DEVICE_INFO handler
//
// Copied the microsoft common - OALHalGetDeviceInfo to OALGetDeviceInfo

#ifdef OSV_PPC
// platform information for PPC
#define TOTALPLATFORMS          2
const DWORD HALTotalPlatforms = TOTALPLATFORMS;
const PLATFORMVERSION HALPlatformVer[TOTALPLATFORMS] = {{4, 0}, {5, 0}};
#elif defined OSV_SP
// platform information for Smartfon
#define TOTALPLATFORMS          1
const DWORD HALTotalPlatforms = TOTALPLATFORMS;
const PLATFORMVERSION HALPlatformVer[TOTALPLATFORMS] = {{4, 0}};
#else
// platform information for WinCE.net
#define TOTALPLATFORMS          1
const PLATFORMVERSION HALPlatformVer[TOTALPLATFORMS] = {{CE_MAJOR_VER, CE_MINOR_VER}};
#endif


BOOL OALGetDeviceInfo(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize
) {
    BOOL rc = FALSE;
    UINT32 length;


    OALMSG(OAL_IOCTL&&OAL_FUNC, (L"+OALIoCtlHalGetDeviceInfo(...)\r\n"));

    // Validate inputs
    if (pInpBuffer == NULL || inpSize < sizeof(UINT32)) {
        NKSetLastError(ERROR_INVALID_PARAMETER);
        OALMSG(OAL_ERROR, (
            L"ERROR: OALIoCtlHalGetDeviceInfo: Invalid parameter\r\n"
        ));
        goto cleanUp;
    }

    __try
    {

        // Process according to input request
        switch (*(UINT32*)pInpBuffer) {
        case SPI_GETPLATFORMTYPE:
            // Validate output buffer size
#if defined( OSV_PPC ) || defined( OSV_SP )
            // on PocketPC and Smartphone platforms the platform type
            // string may contain NULLs and is terminated with a
            // double NULL.
            {
                const WCHAR* pwszTemp;
                UINT32 nSubStrLen;

                length = sizeof(WCHAR);
                pwszTemp = g_oalIoCtlPlatformType;
                while( *pwszTemp != 0 )
                {
                    nSubStrLen = NKwcslen(pwszTemp) + 1;
                    length += nSubStrLen * sizeof(WCHAR);
                    pwszTemp += nSubStrLen;
                }
            }
#else
            length = (NKwcslen(g_oalIoCtlPlatformType) + 1) * sizeof(WCHAR);
#endif
            // Return required size
            if (pOutSize != NULL) *pOutSize = length;
            // If there isn't output buffer or it is small return error
            if (pOutBuffer == NULL || outSize < length) {
                NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIoCtlHalGetDeviceInfo: Insufficient buffer\r\n"
                ));
                break;
            }
            // Copy requested data to caller's buffer, set output length
            memcpy(pOutBuffer, g_oalIoCtlPlatformType, length);
            rc = TRUE;
            break;

        case SPI_GETOEMINFO:
            // Validate output buffer size
            length = (NKwcslen(g_oalIoCtlPlatformOEM) + 1) * sizeof(WCHAR);
            // Return required size
            if (pOutSize != NULL) *pOutSize = length;
            // If there isn't output buffer or it is small return error
            if (pOutBuffer == NULL || outSize < length) {
                NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIoCtlHalGetDeviceInfo: Insufficient buffer\r\n"
                ));
                break;
            }
            // Copy requested data to caller's buffer, set output length
            memcpy(pOutBuffer, g_oalIoCtlPlatformOEM, length);
            rc = TRUE;
            break;

        case SPI_GETPLATFORMVERSION:
            length = sizeof HALPlatformVer;
            // Return required size
            if (pOutSize != NULL) *pOutSize = length;
            // Return platform version
            if (outSize < length)
            {
                NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIoCtlHalGetDeviceInfo: Insufficient buffer\r\n"
                ));
            }
            else if (!pOutBuffer)
            {
                NKSetLastError(ERROR_INVALID_PARAMETER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIoCtlHalGetDeviceInfo: Invalid parameter\r\n"
                ));
            }
            else
            {
                memcpy(pOutBuffer, HALPlatformVer, length);
                rc = TRUE;
            }
            break;

        case SPI_GETPLATFORMMANUFACTURER:
            // validate if this action code is supported
            if (g_oalIoCtlPlatformManufacturer == NULL)
            {
                NKSetLastError(ERROR_NOT_SUPPORTED);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIoCtlHalGetDeviceInfo: Feature not supported\r\n"
                ));
                break;
            }
            // Validate output buffer size
            length = (NKwcslen(g_oalIoCtlPlatformManufacturer) + 1) * sizeof(WCHAR);
            // Return required size
            if (pOutSize != NULL) *pOutSize = length;
            // If there isn't output buffer or it is small return error
            if (pOutBuffer == NULL || outSize < length) {
                NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIoCtlHalGetDeviceInfo: Insufficient buffer\r\n"
                ));
                break;
            }
            // Copy requested data to caller's buffer, set output length
            memcpy(pOutBuffer, g_oalIoCtlPlatformManufacturer, length);
            rc = TRUE;
            break;

        case SPI_GETPLATFORMNAME:
            // validate if this action code is supported
            if (g_oalIoCtlPlatformName == NULL)
            {
                NKSetLastError(ERROR_NOT_SUPPORTED);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIoCtlHalGetDeviceInfo: Feature not supported\r\n"
                ));
                break;
            }

            // Validate output buffer size
            length = (NKwcslen(g_oalIoCtlPlatformName) + 1) * sizeof(WCHAR);
            // Return required size
            if (pOutSize != NULL) *pOutSize = length;
            // If there isn't output buffer or it is small return error
            if (pOutBuffer == NULL || outSize < length) {
                NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIoCtlHalGetDeviceInfo: Insufficient buffer\r\n"
                ));
                break;
            }
            // Copy requested data to caller's buffer, set output length
            memcpy(pOutBuffer, g_oalIoCtlPlatformName, length);
            rc = TRUE;
            break;

        default:
            OALMSG(OAL_ERROR, (
                L"ERROR: OALIoCtlHalGetDeviceInfo: Invalid request\r\n"
            ));
            break;
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        NKSetLastError(ERROR_INVALID_USER_BUFFER);
        OALMSG(OAL_WARN, (
            L"WARN: OALGetDeviceInfo: User buffer access violation!\r\n"
        ));
        goto cleanUp;
    }

cleanUp:
    // Indicate status
    OALMSG(OAL_FUNC&&OAL_IOCTL, (
        L"-OALIoCtlHalGetDeviceInfo(rc = %d)\r\n", rc
    ));
    return rc;
}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlProcessorInfoLittleton
//
//  Implements the IOCTL_PROCESSOR_INFORMATION handler.
//
BOOL OALIoCtlProcessorInfoLittleton(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize
) {
    BOOL rc = FALSE;
    PROCESSOR_INFO *pInfo = (PROCESSOR_INFO*)pOutBuffer;
    UINT32 length1, length2, length3;

    OALMSG(OAL_FUNC, (L"+OALIoCtlProcessorInfoLittleton(...)\r\n"));

    __try
    {
        // Set required/returned size if pointer isn't NULL
        if (pOutSize != NULL) *pOutSize = sizeof(PROCESSOR_INFO);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        NKSetLastError(ERROR_INVALID_PARAMETER);
        OALMSG(OAL_WARN, (
            L"WARN: OALIoCtlProcessorInfoLittleton: Invalid pOutSize parameter\r\n"
        ));
        goto cleanUp;
    }

    // Validate inputs
    if (pOutBuffer == NULL || outSize < sizeof(PROCESSOR_INFO)) {
        NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
        OALMSG(OAL_WARN, (
            L"WARN: OALIoCtlProcessorInfoLittleton: Buffer too small\r\n"
        ));
        goto cleanUp;
    }


    __try
    {
        // Verify OAL lengths
        length1 = (NKwcslen(g_oalIoCtlProcessorCore) + 1) * sizeof(WCHAR);
        if (length1 > sizeof(pInfo->szProcessCore)) {
            OALMSG(OAL_ERROR, (
                L"ERROR:OALIoCtlProcessorInfoLittleton: Core value too big\r\n"
            ));
            goto cleanUp;
        }

        length2 = (NKwcslen(g_oalIoCtlProcessorName) + 1) * sizeof(WCHAR);
        if (length2 > sizeof(pInfo->szProcessorName)) {
            OALMSG(OAL_ERROR, (
                L"ERROR:OALIoCtlProcessorInfoLittleton: Name value too big\r\n"
            ));
            goto cleanUp;
        }

            length3 = (NKwcslen(g_oalIoCtlProcessorVendor) + 1) * sizeof(WCHAR);
        if (length3 > sizeof(pInfo->szVendor)) {
            OALMSG(OAL_ERROR, (
                L"ERROR:OALIoCtlProcessorInfoLittleton: Vendor value too big\r\n"
            ));
            goto cleanUp;
        }

        // Copy in processor information
        memset(pInfo, 0, sizeof(PROCESSOR_INFO));
        pInfo->wVersion = 1;
        memcpy(pInfo->szProcessCore, g_oalIoCtlProcessorCore, length1);
        memcpy(pInfo->szProcessorName, g_oalIoCtlProcessorName, length2);
        memcpy(pInfo->szVendor, g_oalIoCtlProcessorVendor, length3);
        pInfo->dwInstructionSet = g_oalIoCtlInstructionSet;
        pInfo->dwClockSpeed  = g_oalIoCtlClockSpeed;

    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        NKSetLastError(ERROR_INVALID_USER_BUFFER);
        OALMSG(OAL_WARN, (
            L"WARN: OALIoCtlProcessorInfoLittleton: User buffer access violation!\r\n"
        ));
        goto cleanUp;
    }

    // Indicate success
    rc = TRUE;

cleanUp:
    OALMSG(OAL_FUNC, (L"-OALIoCtlProcessorInfoLittleton(rc = %d)\r\n", rc));
    return rc;
}

//------------------------------------------------------------------------------
//
//  Function: OALIoCtlHalQueryFormatPartition
//
BOOL OALIoCtlHalQueryFormatPartition(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize
) {
    BOOL rc = FALSE;
    BOOL* bEbootForcedUpdate;

    OALMSG(1, (TEXT("QueryFormatPartition called now\r\n")));
    // Check buffer size
    if (pOutSize != NULL) *pOutSize = sizeof(BOOL);
    if (pOutBuffer == NULL || outSize < sizeof(BOOL)) {
        NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
        OALMSG(OAL_WARN, (L"WARN: OALIoCtlHalQueryFormatPartition: "
            L"Buffer too small\r\n"
        ));
        goto cleanUp;
    }

    // Check if the right softkey is pressed
//    if (ReadKeyPad() & KEY_TSOFT2) {
//        OALMSG(TRUE, (L"INFO: TSOFT2 key pressed - erase TFAT\r\n"));
//        *(BOOL*)pOutBuffer = TRUE;
//    } else {
    // Check to see if EBoot is forcing us to format
    bEbootForcedUpdate = OALArgsQuery(BSP_ARGS_QUERY_FORMATPARTITION);
    if (*bEbootForcedUpdate == TRUE)
    {
        OALMSG(1, (TEXT("EBoot forced format partition format\r\n")));
        *(BOOL*)pOutBuffer = TRUE;
    }
    else
    {
        *(BOOL*)pOutBuffer = FALSE;
    }
    OALMSG(1, (TEXT("OALQueryFormatPartition return %d\r\n"), *(BOOL*)pOutBuffer));
//    }

    rc = TRUE;
    //rc = *(BOOL*)pOutBuffer; // Should reflect the return value
cleanUp:
    return rc;
}


//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalSetUpdateMode
//
//  Implements the IOCTL_HAL_UPDATE_MODE handler.
//  This function gets/sets the RAM-based update mode flag.
//
BOOL OALIoCtlHalSetUpdateMode (
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize
) {
    BOOL rc = FALSE;
    BOOL *pfUpdateMode = NULL;

    // verify the input
    if (!pInpBuffer || inpSize != sizeof(BOOL))
    {
        NKSetLastError(ERROR_INVALID_PARAMETER);
        OALMSG(OAL_WARN, (L"ERROR: OALIoCtlHalUpdateMode: Invalid input buffer\r\n"));
        goto cleanUp;
    }

    // Get Update Mode flag from BSP Args
    pfUpdateMode = (BOOL *) OALArgsQuery(OAL_ARGS_QUERY_UPDATEMODE);

    // if there is no update mode flag, fail
    if (pfUpdateMode == NULL)
    {
        NKSetLastError(ERROR_NOT_SUPPORTED);
        OALMSG(OAL_WARN, (L"ERROR: OALIoCtlHalUpdateMode: Device doesn't support Update Mode\r\n"));
        goto cleanUp;
    }

    // update the flag
    *pfUpdateMode = *((BOOL *) pInpBuffer);
    NKDbgPrintfW(TEXT("OALIoCtlUpdateMode set to %d\r\n"), *pfUpdateMode);

    // Update the EBOOT Cfg FLASH as well
    FlashUpdateMode((BOOL)(*pfUpdateMode));


    // Done
    rc = TRUE;

cleanUp:
    return rc;
}


BOOL OALIoCtlHalGetBspArgs (
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
    BOOL rc = FALSE;
    BSP_ARGS *pArgs;

    // verify the input



    // Check buffer size
    if (pOutSize != NULL) *pOutSize = sizeof(BSP_ARGS);

    if (pOutBuffer == NULL || outSize < sizeof(BSP_ARGS)) {
        NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
        OALMSG(OAL_WARN, (L"WARN: OALIoCtlHalGetBspArgs: "
            L"Buffer too small\r\n"
        ));
        goto cleanUp;
    }

    pArgs = (BSP_ARGS *)IMAGE_SHARE_ARGS_UA_START;

    *(BSP_ARGS *)pOutBuffer =*(BSP_ARGS *)IMAGE_SHARE_ARGS_UA_START;
    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

//XLLP_MINI_LCD_T xllp_mini_lcd;
//volatile UINT32* LCD_FDADR0;
volatile XLLP_LCD_REGISTERS_T* LCDRegs;

//#define MINI_LCD_CODESIZE   0x1000
//#define MINI_LCD_OFFSET 0x10000
//#define MINI_LCD_FRAME_PA (MONAHANS_BASE_REG_PA_IMSTORAGE + MINI_LCD_OFFSET)
//#define IM_SIZE 0x40000 	// in Monahans L, sram size is 256k

BOOL OALIoCtlHalSetLcd(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
    BOOL rc = FALSE;
    XLLP_LCD_T * p_lcd;

    // verify the input
    if (!pInpBuffer || inpSize != sizeof(XLLP_LCD_T))
    {
        NKSetLastError(ERROR_INVALID_PARAMETER);
        OALMSG(OAL_WARN, (L"ERROR: OALIoCtlHalSetLcdInfo: Invalid input buffer\r\n"));
        goto cleanUp;
    }

    p_lcd = (XLLP_LCD_T *)pInpBuffer;
    tft_panel = *(tft_panel_t *)(p_lcd->panel);

    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalGetRandomSeed
//
//  This function is called by the kernel to get the hardware random seed value
//
BOOL OALIoCtlHalGetRandomSeed(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
RETAILMSG(1, (L"OALIoCtlHalGetRandomSeed\r\n"));

    // Do nothing for now.
    //
    return(TRUE);

}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalGetHiveCleanFlag
//
//  This function is called by the kernel to determine if it should start with
//  a clean registry hive
//
BOOL OALIoCtlHalGetHiveCleanFlag(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
RETAILMSG(1, (L"OALIoCtlHalGetHiveCleanFlag\r\n"));

    // Do nothing for now.
    //
    return(TRUE);

}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalPreSuspend
//
//  This function is called by the kernel just prior to rebooting or suspending
//  and is the last chance the OAL will get to perform cleanup tasks while
//  in a threaded mode.
//
BOOL OALIoCtlHalPreSuspend(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
RETAILMSG(1, (L"OALIoCtlHalPreSuspend\r\n"));

    // Do nothing for now.
    //
    return(TRUE);

}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalPreSuspend
//
//  This function is called by the kernel just prior to rebooting or suspending
//  and is the last chance the OAL will get to perform cleanup tasks while
//  in a threaded mode.
//
BOOL OALIoCtlHalProfile(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
RETAILMSG(1, (L"OALIoCtlHalProfile\r\n"));

    // Do nothing for now.
    //
    return(TRUE);

}


#include <ipmioctl.h>
#include <ipm_xllp_pm_dvfm.h>

extern BOOL IPM_StartIdleProfiler(UINT32 windowSizeMs,UINT32 *thresholdArray,UINT32 numThresholds,UINT32 ticksPerMS,UINT32 *compareTicks);
extern BOOL IPM_GetUtilization(UINT32 *utilbuf);
extern BOOL IPM_GetUtilization2(IPMIPROFUTILSTRUCT *buff);
extern BOOL IPM_AllowHighProfEvents(BOOL enable);
extern BOOL IPM_AllowLowProfEvents(BOOL enable);
extern BOOL IPM_StartIdleCapture(UINT32 bounds);
extern BOOL IPM_EndIdleCapture(IPMIPROFCAPTURE * buffer, UINT32 * idleCount);
extern void IPM_FCS_PREAMBLE(const XLLP_PM_FV_INFO *currentOP, const XLLP_PM_FV_INFO *nextOP, BOOL HSIOOnly, BOOL ChangeHSIO);
extern void IPM_FCS_POSTFIX(const XLLP_PM_FV_INFO *currentOP, const XLLP_PM_FV_INFO *nextOP, BOOL HSIOOnly, BOOL ChangeHSIO);
extern void IPM_DoFCS(XLLP_PM_FV_INFO *nextOP, BOOL HSIOOnly, BOOL changeHSIO, BOOL inIdle);
extern BOOL IPM_StopProfilers(void);
extern BOOL IPM_StartProfilers(void);
extern UINT32 gIPM_LCDEOF_FCS;
extern XLLP_PM_FV_INFO gIPM_Current_OP;
extern XLLP_PM_FV_INFO gIPM_Next_OP;
extern XLLP_PM_FV_INFO por_ops[IPM_OPERATING_POINTS_COUNT];
extern IPM_PRODUCT_OP  dwCurrentOP;
extern DWORD   ipm_lpm_state;
extern DWORD iprof_sysintr;


extern BSP_ARGS *g_pBSPArgs;
//
// We use pInpBuffer casted to IPMKIOCTL type
BOOL OALIOCtlIPM(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
    UINT32 *pInBuffIPM = (UINT32 *)pInpBuffer;
    UINT32 IPMIoctlType;
    __try
    {
         if (pOutSize)
            *pOutSize = 0;
         else
            return(FALSE);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        NKSetLastError(ERROR_INVALID_PARAMETER);
        OALMSG(OAL_WARN, (
            L"WARN: OALIOCtlIPM: Invalid out buffer length parameter\r\n"
        ));
        return(FALSE);
    }

    if((inpSize == 0) || (!pInpBuffer))
    {
        return(FALSE);
    }

    // Parse the input buffer to determine which type of IPM message we have
    //  First UINT32 MUST be the IPM ioctl type.  Then, once learned, we re-cast.
    //

    __try
    {
        IPMIoctlType = *pInBuffIPM;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        NKSetLastError(ERROR_INVALID_PARAMETER);
        OALMSG(OAL_WARN, (
            L"WARN: OALIOCtlIPM: Invalid parameter\r\n"
        ));
        return(FALSE);
    }

    switch(IPMIoctlType)
    {
    case IPROF_INIT:
        {
            IPMIPFROFINITSTRUCT *pIProfPayload = (IPMIPFROFINITSTRUCT *)pInBuffIPM;
            __try
            {
                iprof_sysintr = pIProfPayload->iprof_sys_intr;
                RETAILMSG(1, (L"OALIOCtlIPM:  Received IPROF_INIT\r\n"));
                if(!pOutBuffer)
                {
                    RETAILMSG(1, (L"OALIOCtlIPM:  pOutBuffer is NULL!\r\n"));
                    return(FALSE);
                }
                // Pass it on
                if(!IPM_StartIdleProfiler(pIProfPayload->windowSizeMs, (UINT32 *)pOutBuffer, pIProfPayload->numThresholds, pIProfPayload->ticksPerMs, (UINT32 *)pOutBuffer))
                {
                    OALMSG(OAL_WARN, (L"WARN: OALIOCtlIPM:  IPM_StartIdleProfiler() failed!\r\n"));
                    return(FALSE);
                }
                *pOutSize = sizeof(UINT32);
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                NKSetLastError(ERROR_INVALID_PARAMETER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIOCtlIPM: Invalid parameter\r\n"
                ));
                return(FALSE);
            }
            return(TRUE);
            break;
        }

    case IPROF_GET_UTIL:
        {
            __try
            {
                //UINT32 cyclecount;
                if(!pOutBuffer)
                {
                    RETAILMSG(1, (L"OALIOCtlIPM:  pOutBuffer is NULL!\r\n"));
                    return(FALSE);
                }
                // Pass it on
                if(!IPM_GetUtilization((UINT32 *)pOutBuffer))
                {
                    RETAILMSG(1, (L"OALIOCtlIPM:  IPM_GetUtilization() failed!\r\n"));
                    return(FALSE);
                }
                //RETAILMSG(1, (L"OALIOCtlIPM:  IPM_GetUtilization cyclecount = %d\r\n", cyclecount));
                *pOutSize = sizeof(UINT32);
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                NKSetLastError(ERROR_INVALID_PARAMETER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIOCtlIPM: Invalid parameter\r\n"
                ));
                return(FALSE);
            }
            return(TRUE);
            break;
        }

    case IPROF_GET_THRESH_INFO:
        {
            __try
            {
                if(!pOutBuffer)
                {
                    RETAILMSG(1, (L"OALIOCtlIPM:  pOutBuffer is NULL!\r\n"));
                    return(FALSE);
                }
                // Pass it on
                if(!IPM_GetUtilization2((IPMIPROFUTILSTRUCT *)pOutBuffer))
                {
                    RETAILMSG(1, (L"OALIOCtlIPM:  IPM_GetUtilization2() failed!\r\n"));
                    return(FALSE);
                }
                //                buff = (IPMIPROFUTILSTRUCT *)pOutBuffer;
                *pOutSize = sizeof(IPMIPROFUTILSTRUCT);
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                NKSetLastError(ERROR_INVALID_PARAMETER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIOCtlIPM: Invalid parameter\r\n"
                ));
                return(FALSE);
            }

            return(TRUE);
            break;
        }
    case IPROF_ADJUST_THRESH_EVENT:
        {
            IPMADJUSTTHRESHEVENTSTRUCT * payload = (IPMADJUSTTHRESHEVENTSTRUCT *)pInBuffIPM;
            IPMTHRESHOLD threshtochange;
            BOOL enable;

            __try
            {
                threshtochange = payload->threshtochange;
                enable = payload->enable;
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                NKSetLastError(ERROR_INVALID_PARAMETER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIOCtlIPM: Invalid parameter\r\n"
                ));
                return(FALSE);
            }

            switch (threshtochange)
            {
            case IPROFT_HIGH:
                IPM_AllowHighProfEvents(enable);
                break;
            case IPROFT_LOW:
                IPM_AllowLowProfEvents(enable);
                break;
            default:
                break;
            }
            return(TRUE);
            break;
        }
    case SET_IDLE_LPM_CONFIG:
        {
            IPMSETIDLELPMSTRUCT * payload = (IPMSETIDLELPMSTRUCT *) pInBuffIPM;
            __try
            {
                ipm_lpm_state = payload->dwIpmApprovedLpm;
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                NKSetLastError(ERROR_INVALID_PARAMETER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIOCtlIPM: Invalid parameter\r\n"
                ));
                return(FALSE);
            }
            return(TRUE);
            break;
        }
    case INIT_OPS:
        {

            IPMINITOPSSTRUCT *pInitOpsPayload = (IPMINITOPSSTRUCT *)pInBuffIPM;
            // Populate array containing operating points for idle.
            __try
            {
                memcpy( por_ops, pInitOpsPayload->por_ops, sizeof(XLLP_PM_FV_INFO)*IPM_OPERATING_POINTS_COUNT);
                dwCurrentOP =  pInitOpsPayload->dwCurrentOP;
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                NKSetLastError(ERROR_INVALID_PARAMETER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIOCtlIPM: Invalid parameter\r\n"
                ));
                return(FALSE);
            }
            return(TRUE);
            break;
        }
    case GET_OP:
        {
            // This will return the current op
            //  Will eventually be done by XLLP
            //
            volatile XLLP_CLKMGR_T *pCLKS = (volatile XLLP_CLKMGR_T *)OALPAtoVA(MONAHANS_BASE_REG_PA_CLKMGR, FALSE);
            volatile XLLP_SMEM_REGISTER_T *pSMC = (volatile XLLP_SMEM_REGISTER_T *)OALPAtoVA(MONAHANS_BASE_REG_PA_SMEMC, FALSE);
            XLLP_PM_FV_INFO *currOP = (XLLP_PM_FV_INFO *)pOutBuffer;

            if(NULL==pOutBuffer)
                return(FALSE);

            __try
            {
                currOP->d0cs    = (pCLKS->acsr & 0x04000000) >> 26;
                currOP->dmcfs   = (pCLKS->acsr & 0x3000) >> 12;
                currOP->hss     = (pCLKS->acsr & 0xC000) >> 14;
                currOP->sflfs   = (pCLKS->acsr & 0xC0000) >> 18;
                currOP->smcfs   = (pCLKS->acsr & 0x03800000) >> 23;
                currOP->xn      = (pCLKS->acsr & 0x700) >> 8;
                currOP->xl      = (pCLKS->acsr & 0x1F);
                currOP->vauf    = (pCLKS->acsr & (0x3u << 21)) >> 21;   //Mved accelerator clock

                currOP->df_clk  = (pSMC->memclkcfg & 0x70000) >> 16;
                currOP->empi_clk = (pSMC->memclkcfg & 0x7);

                currOP->vcc_core = 1400;            // *Note: we query PMIC later.  Set to max.
                currOP->vcc_sram = 1400;            // *Note: we query PMIC later.  Set to max.
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                NKSetLastError(ERROR_INVALID_PARAMETER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIOCtlIPM: Invalid parameter\r\n"
                ));
                return(FALSE);
            }
            return(TRUE);
            break;
        }

    case SET_OP:
        //
        // This will set the non-HSIO frequency.  We need to be in kernel mode.
        //

        {
            volatile XLLP_LCD_REGISTERS_T *pLCDC = (volatile XLLP_LCD_REGISTERS_T *)OALPAtoVA(MONAHANS_BASE_REG_PA_LCD, FALSE);
            IPMSETOPSTRUCT  *pSetOPPayload = (IPMSETOPSTRUCT  *)pInpBuffer;         // contains current OP
            XLLP_PM_FV_INFO *pNextOP = (XLLP_PM_FV_INFO *)pOutBuffer;               // contains next OP

            //RETAILMSG(1, (L"OALIOCtlIPM:  Received SET_OP\r\n"));
            //RETAILMSG(1, (L"    OALIOCtlIPM:  current XL=%d, HSS=%d, scaleHSIO=%d\r\n", pSetOPPayload->curr_OP.xl, pSetOPPayload->curr_OP.hss, pSetOPPayload->scaleHSIO));
            //RETAILMSG(1, (L"    OALIOCtlIPM:  next XL=%d, HSS=%d\r\n", pNextOP->xl, pNextOP->hss));


            if(NULL==pOutBuffer)
                return(FALSE);


            // If scaleHSIO is TRUE, we will be changing all operating point parameters
            //
            //
            __try
            {
                //RETAILMSG(1, (L"OALIOCtlIPM:  Received SET_OP\r\n"));
                //RETAILMSG(1, (L"    OALIOCtlIPM:  current XL=%d, HSS=%d, scaleHSIO=%d\r\n", pSetOPPayload->curr_OP.xl, pSetOPPayload->curr_OP.hss, pSetOPPayload->scaleHSIO));
                //RETAILMSG(1, (L"    OALIOCtlIPM:  next XL=%d, HSS=%d\r\n", pNextOP->xl, pNextOP->hss));
                if(pSetOPPayload->scaleHSIO)
                {
                    gIPM_LCDEOF_FCS = 0;                    // No need to change HSIO.  Safety- i dont expect the bsp to care in this case.

                    {

                        IPM_FCS_PREAMBLE(&pSetOPPayload->curr_OP, pNextOP, FALSE, TRUE);

                        // Do all bus FCS
                        //
                        IPM_DoFCS(pNextOP, FALSE, TRUE, FALSE);

                        IPM_FCS_POSTFIX(&pSetOPPayload->curr_OP, pNextOP, FALSE, TRUE);
                    }
                    dwCurrentOP = pSetOPPayload->dwNextOP;
                }
                else
                {
                    // Disable EOF ints at the LCDC
                    //
                    pLCDC->lccr0 |= XLLP_LCCR0_EOFM0;

                    // Update kernel globs for ISR to access
                    //
                    memcpy(&gIPM_Current_OP, &pSetOPPayload->curr_OP, sizeof(XLLP_PM_FV_INFO));
                    memcpy(&gIPM_Next_OP, pNextOP, sizeof(XLLP_PM_FV_INFO));

                    // execute code needed right before the FCS
                    {
                        IPM_FCS_PREAMBLE(&pSetOPPayload->curr_OP, pNextOP, FALSE, FALSE);

                        // Do non-HSIO FCS
                        //
                        IPM_DoFCS(pNextOP, FALSE, FALSE, FALSE);

                        // execute code needed right after the FCS
                        //
                        IPM_FCS_POSTFIX(&pSetOPPayload->curr_OP, pNextOP, FALSE, TRUE);
                    }

                    //    Need to communicate back to caller so it can avoid the block
                    //
                    gIPM_LCDEOF_FCS = 1;                    // Need to change HSIO; ISR needs to clear

                    //
                    // Re-enable EOF ints after clearing any status to avoid stale interrupt.
                    //
                    pLCDC->lcsr0 = XLLP_LCSR0_EOF0;
                    pLCDC->lccr0 &= ~XLLP_LCCR0_EOFM0;
                }
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                NKSetLastError(ERROR_INVALID_PARAMETER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIOCtlIPM: Invalid parameter\r\n"
                ));
                return(FALSE);
            }
            return(TRUE);
            break;
        }
    case IPROF_START_CAPTURE:
        {
            IPMSTARTCAPTURE * payload = (IPMSTARTCAPTURE *)pInpBuffer;

            //RETAILMSG(1, (L"IPROF_START_CAPTURE+\r\n"));

            __try
            {
                IPM_StartIdleCapture(payload->sampleBufferMax);
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                NKSetLastError(ERROR_INVALID_PARAMETER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIOCtlIPM: Invalid parameter\r\n"
                ));
                return(FALSE);
            }

            return (TRUE);
            break;
        }
    case IPROF_END_CAPTURE:
        {
            UINT32 idleCount;
            __try
            {
                IPM_EndIdleCapture((IPMIPROFCAPTURE *)pOutBuffer , &idleCount);
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                NKSetLastError(ERROR_INVALID_PARAMETER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIOCtlIPM: Invalid parameter\r\n"
                ));
                return(FALSE);
            }
            return (TRUE);
            break;
        }
    case GET_BOOT_INFO:
        {
            //
            // This allows IPM to gain boot-time information that cannot
            //  easily be communicated any other way.
            //
            extern BSP_ARGS *g_pBSPArgs;
            IPMBOOTINFO *info_out = (IPMBOOTINFO *) pOutBuffer;
            OAL_KITL_ARGS *pKITLArgs = (OAL_KITL_ARGS*) OALArgsQuery(OAL_ARGS_QUERY_KITL);

            RETAILMSG(1, (L"OALIOCtlIPM:  Received GET_BOOT_INFO.  OALArgsQuery returned: flags=0x%x, devLoc.PhysicalLoc=0x%x.  OALPAtoVA(MONAHANS_BASE_REG_PA_UDC, FALSE) = 0x%x\r\n", pKITLArgs->flags, pKITLArgs->devLoc.PhysicalLoc, OALPAtoVA(MONAHANS_BASE_REG_PA_UDC, FALSE)));

            // Check to see if we are using KITL over any USB.
            // If we are, we will dis-allow D0CS usage.
            // *ISSUE: now cannot change IPM_DEFS and just do an IPM build.  To fix this, should pass in IPM_DEFS values*
            //

            // Default case
            //
            __try
            {
                info_out->D0CSIdleOK = info_out->D1IdleOK = info_out->D2IdleOK = TRUE;
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                NKSetLastError(ERROR_INVALID_PARAMETER);
                OALMSG(OAL_WARN, (
                    L"WARN: OALIOCtlIPM: Invalid parameter\r\n"
                ));
                return(FALSE);
            }

            // Deal with USB KITL inherent restrictions: no D1, D2 or D0CS.
            //
            if((pKITLArgs->flags & OAL_KITL_FLAGS_ENABLED) &&
                ((pKITLArgs->devLoc.PhysicalLoc == (PVOID)MONAHANS_BASE_REG_PA_UDC) ||
                (pKITLArgs->devLoc.PhysicalLoc == (PVOID)MONAHANS_BASE_REG_PA_USB2H)))
            {
                info_out->D0CSIdleOK    = FALSE;               // do not use D0CS as idle replacement
                info_out->D1IdleOK      = FALSE;               // do not use D1 as idle replacement
                info_out->D2IdleOK      = FALSE;               // do not use D2 as idle replacement

                info_out->MinOP = IPM_OP_104MHZ;            // minimum operating point enforced for USB KITL
                RETAILMSG(1, (L"OALIOCtlIPM:  USB KITL present\r\n"));
            }
            else
            {
                info_out->MinOP = SYSTEM_MIN_OP;
            }
            
            if(((g_pBSPArgs->LowPowerModeIdle & BSP_ARGS_IDLE_D0CS) == 0))
            {
                // fill out data structure accordingly
                info_out->D0CSIdleOK = FALSE;               // do not use D0CS as idle replacement
            }
            if(((g_pBSPArgs->LowPowerModeIdle & BSP_ARGS_IDLE_D1) == 0))
            {
                // fill out data structure accordingly
                info_out->D1IdleOK = FALSE;               // do not use D1 as idle replacement
            }
            if(((g_pBSPArgs->LowPowerModeIdle & BSP_ARGS_IDLE_D2) == 0))
            {
                // fill out data structure accordingly
                info_out->D2IdleOK= FALSE;               // do not use D2 as idle replacement
            }

            *pOutSize = sizeof(IPMBOOTINFO);
            return(TRUE);
            break;
        }
    case BLOCK_FC:
        {
            //RETAILMSG(1, (L"OALIOCtlIPM:  Block_FC\r\n"));
            IPM_StopProfilers();
            return(TRUE);
            break;
        }
    case  RELEASE_FC:
        {
            //RETAILMSG(1, (L"OALIOCtlIPM:  RELEASE_FC\r\n"));
            IPM_StartProfilers();
            return(TRUE);
            break;
        }

    default:
        {
            RETAILMSG(1, (L"OALIOCtlIPM:  Unsupported Kernel IOCTL\r\n"));
            return(FALSE);
        }
    }
}


#ifdef OSV_WINCENET
BOOL OALIoCtlHalGetRegSecureKeys(
    UINT32 dwIoControlCode, VOID *lpInBuf, UINT32 nInBufSize, VOID *lpOutBuf,
    UINT32 nOutBufSize, UINT32* lpBytesReturned)
{

    BOOL rc;

  // Not Smartphone or PocketPC build; we have no registry keys to secure
    // First call: return buffer size of 0.  We should not be called again.
    if (!lpInBuf && !nInBufSize && lpOutBuf && (nOutBufSize == sizeof(DWORD)))
    {
        *((DWORD*)lpOutBuf) = (DWORD)0;
        if (lpBytesReturned)
        {
            *lpBytesReturned = sizeof(DWORD);
        }

        rc = TRUE;
    }
    else
    {
        // Invalid args
        NKSetLastError(ERROR_INVALID_PARAMETER);
        rc = FALSE;
    }

    OALMSG(OAL_IOCTL&&OAL_FUNC, (L"-OALIoCtlHalGetRegSecureKeys(rc = %d)\r\n", rc));
    return (rc);
}
#endif

static BOOL OALIoCtlGetPhysicalAdress(
    UINT32 code, VOID *lpInBuf, UINT32 nInBufSize, VOID *lpOutBuf,
    UINT32 nOutBufSize, UINT32 *pOutSize)
{
       OALMSG(OAL_FUNC, (TEXT("OALIoCtlGetPhysicalAdress\r\n")));

        if (!lpInBuf || (nInBufSize != sizeof(DWORD))
        || !lpOutBuf || (nOutBufSize < sizeof(DWORD)))
        {
              return FALSE;
        }
        else
        {
        volatile DWORD * pBuff = (DWORD*) OALPAtoVA(*(DWORD*)lpInBuf, FALSE);

        if(pBuff == NULL)
            return FALSE;


        *(DWORD*)lpOutBuf	= *pBuff;
        }
        if(pOutSize)
        {
              *pOutSize = sizeof(UINT32);
        }

    return TRUE;
}

static BOOL OALIoCtlSetPhysicalAdress(
    UINT32 code, VOID *lpInBuf, UINT32 nInBufSize, VOID *lpOutBuf,
    UINT32 nOutBufSize, UINT32 *pOutSize)
{
    OALMSG(OAL_FUNC, (TEXT("OALIoCtlSetPhysicalAdress\r\n")));

        if (!lpInBuf || (nInBufSize != 2*sizeof(DWORD)))
        {
              return FALSE;
        }
        else
        {
        volatile DWORD * pBuff = (DWORD*) OALPAtoVA(*(DWORD*)lpInBuf, FALSE);
        if(pBuff == NULL)
            return FALSE;

        *(DWORD*)pBuff	= *(((DWORD*)lpInBuf) + 1);
        }
        if(pOutSize)
        {
              *pOutSize = 0;
        }

    return TRUE;
}

//------------------------------------------------------------------------------
//
//  Global: g_oalIoCtlTable[]
//
//  IOCTL handler table. This table includes the IOCTL code/handler pairs
//  defined in the IOCTL configuration file. This global array is exported
//  via oal_ioctl.h and is used by the OAL IOCTL component.
//
const OAL_IOCTL_HANDLER g_oalIoCtlTable[] = {
#include "ioctl_tab.h"
};

//------------------------------------------------------------------------------


